<body></body>
<script>
  let activeEffect = null
  const effectStack = [] //effect栈
  const bucket = new WeakMap()
  //确保ITERATE_KEY是唯一，设置对象的迭代
  const ITERATE_KEY = Symbol()

  function track(target, key) {
    if (!activeEffect) return
    let depMap = bucket.get(target)
    if (!depMap) bucket.set(target, (depMap = new Map()))

    let deps = depMap.get(key)
    if (!deps) depMap.set(key, (deps = new Set()))

    const effectSet = deps.add(activeEffect)
    activeEffect.deps.push(effectSet)
  }

  function trigger(target, key, type) {
    let depMap = bucket.get(target)
    if (!depMap) return
    const effects = depMap.get(key)
    const effectsToRun = new Set()
    // ADD 或 DELETE 操作时，需要触发与ITERATE_KEY 相关联的副作用函数重新执行
    if (type === 'ADD' || type == 'DELETE') {
      const iterateEffects = depMap.get(ITERATE_KEY)
      iterateEffects &&
        iterateEffects.forEach(effectFn => {
          if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
          }
        })
    }

    effects &&
      effects.forEach(effectFn => {
        if (effectFn !== activeEffect) {
          effectsToRun.add(effectFn)
        }
      })

    effectsToRun.forEach(effectFn => {
      if (effectFn?.options?.scheduler) {
        effectFn?.options?.scheduler(effectFn)
      } else {
        effectFn()
      }
    })
  }

  function effect(fn, options) {
    // 这里的 effectFn是一个独立函数，就是每一次effect开辟一个新的空间，使用的是新的指针
    function effectFn() {
      cleanup(effectFn)
      activeEffect = effectFn

      effectStack.push(effectFn)
      const res = fn()
      effectStack.pop(effectFn)
      activeEffect = effectStack[effectStack.length - 1]
      return res
    }
    effectFn.options = options
    effectFn.deps = [] //反向收集 effectSet的引用
    if (!effectFn?.options?.lazy) effectFn()
    return effectFn
  }
  function cleanup(effectFn) {
    effectFn.deps.forEach(depSet => depSet.delete(effectFn))
    effectFn.deps.length = 0 //遍历所有的 Set集合删除同一个副作用
  }

  const jobQueue = new Set() // 任务队列（自动去重）
  let isFlushing = false // 队列刷新状态
  // 任务队列控制逻辑
  function flushJob() {
    if (isFlushing) return
    isFlushing = true // 微任务触发器
    Promise.resolve()
      .then(() => jobQueue.forEach(job => job()))
      .finally(() => (isFlushing = false))
  }

  // computed:把 getter 作为副作用函数，创建一个 lazy 的 effect
  function computed(getter) {
    let value // 缓存值
    let dirty = true // 标记是否需要重新计算

    const effectFn = effect(getter, {
      lazy: true, // 延迟执行
      scheduler() {
        dirty = true // 依赖变化时标记为需要重新计算
        trigger(obj, 'value') // 触发依赖更新
      }
    })

    const obj = {
      get value() {
        if (dirty) {
          value = effectFn() // 执行计算
          dirty = false // 标记为已计算
        }
        track(obj, 'value') // 追踪读取操作
        return value
      }
    }
    return obj
  }

  // 遍历 obj 对象及其所有嵌套属性
  function traverse(value, seen = new Set()) {
    if (typeof value !== 'object' || value === null || seen.has(value)) return
    seen.add(value) //标记它已被遍历
    for (const k in value) {
      traverse(value[k], seen)
    }
    return value
  }
  // watch 函数接收 3个参数，source 是响应式数据，cb是回调函数,options配置
  function watch(source, cb, options = {}) {
    let getter
    if (typeof source === 'function') getter = source
    else getter = () => traverse(source)
    let oldValue, newValue

    let cleanup //误区：这里的 oninvalidata不会watch直接调用，是开发者在watch使用过程去调用
    const oninvalidate = fn => {
      cleanup = fn
    }
    const job = () => {
      newValue = effectFn()
      if (cleanup) cleanup() //在回调函数cd之前，先调用注册过期回调
      cb(newValue, oldValue, oninvalidate) //将 onInvalidate 作为回调函数的第三个参数，以便用户使用
      oldValue = newValue
    }
    const effectFn = effect(() => getter(), {
      lazy: true,
      scheduler: job
    })
    if (options.immediate) job()
    else oldValue = effectFn()
  }

  // reactive
  function reactive(obj) {
    return new Proxy(obj, {
      get(target, key, receiver) {
        // 代理对象可以通过raw 属性访问原始数据
        if (key === 'raw') {
          return target
        }
        // console.log(`get操作`)
        track(target, key)
        // 得到原始值
        let res = Reflect.get(target, key, receiver)
        if (typeof res === 'object' && res !== null) {
          //调用 reactive 将结果包装成响应式数据并返回
          return reactive(res)
        }
        // 返回 res
        return res
      },
      set(target, key, newVal, receiver) {
        // 先收集旧的值
        const oldVal = target[key]
        //通过hasOwnProperty判断操作类型，只有添加属性时触发副作用函数，避免不必要的性能开销
        const type = Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
        const res = Reflect.set(target, key, newVal, receiver)
        //target===receiver.raw说明receiver就是target的代理对象
        if (target === receiver.raw) {
          // 比较新值与旧值，只要当不全等的时候才触发响应
          if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
            // console.log(`set操作`)
            trigger(target, key, type)
          }
        }
        return res
      },
      // has控制属性存在性 in
      has(target, key) {
        console.log(`in操作`)
        track(target, key)
        return Reflect.has(target, key)
      },
      // Object的拦截for...in、Object.key\ ownKeys控制属性的枚举顺序与内容
      ownKeys(target, key) {
        console.log('%c对象迭代', 'color: purple; font-size: 30px')
        track(target, ITERATE_KEY)
        return Reflect.ownKeys(target)
      },
      // 成功删除对象自己的属性时触发更新，删除不存在属性时返回 false
      deleteProperty(target, key) {
        const hadKey = Object.prototype.hasOwnProperty.call(target, key)
        const res = Reflect.deleteProperty(target, key)
        if (res && hadKey) {
          trigger(target, key, 'DELETE')
        }
        return res
      }
    })
  }

  const obj = reactive({foo: {bar: 1}})
  effect(() => {
    console.log(obj.foo.bar)
  })
  obj.foo.bar = 2 //没有被更新，返回一个reactive，属性也递归深度代理
</script>
